package com.ccc.netty.upload.server.handler;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import com.ccc.netty.upload.ByteUtils;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class TcpServerHandler extends ChannelInboundHandlerAdapter {

	// 文件目录
	public static final String BASE_PATH = "D:/test-upload2/";

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		ByteBuf byteBuf = (ByteBuf) msg;

		// 判断报文长度
		int bufLen = byteBuf.readableBytes();

		log.info("msg length: {}", bufLen);

		byte[] bytes = new byte[bufLen];
		// [73, 32, 97, 109, 32, 99, 108, 105, 101, 110, 116, 33]
		byteBuf.getBytes(byteBuf.readerIndex(), bytes);

		/**
		 * 这里所有解析都应该进行判断是否为空是否有效，因为是demo所以不写
		 */

		// 前面4个字节已经在decode阶段跳过了
		byte msgType = byteBuf.readByte();// 读取一个字节为消息类型

		switch (msgType) {
		case 0x01:
			upload(byteBuf);
			break;
		case 0x02:
			merge(byteBuf);
			break;
		}

		ctx.writeAndFlush("ok");

		// 必须释放，如果继承simplechannelinboundhandler会自动释放，但是报文处理写在channelRead0
		ReferenceCountUtil.release(msg);
	}

	/**
	 * 报错 处理事件
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

		log.error("# 客户端连接  Netty 出错... %s", cause);
	}

	/**
	 * 收取客户端上传的文件
	 * 
	 * @param buf
	 */
	private void upload(ByteBuf buf) {
		// 业务的报文处理
		// 解析
		// 定义协议 PS：x表示占几个字节
		// [[版本x2][报文总长度x2][消息类型x1][文件标识x16][分片序号x2][文件总大小x8][分片文件长度x2][分片文件数据]]
		// 消息类型 0x01 上传文件 0x02 合并文件

		byte[] filenameBytes = new byte[16];
		buf.readBytes(filenameBytes);// 读取16字节是文件名
		String filename = ByteUtils.bytesToHex(filenameBytes);// 我这里是md5不会有中文没有乱码的问题

		short fileseq = buf.readShort();// 读取2字节是分片序号

		long filesize = buf.readLong();// 读取8字节是文件总大小

		short chunksize = buf.readShort();// 读取2字节是分片文件长度

		byte[] dataBytes = new byte[chunksize];
		buf.readBytes(dataBytes);

		createFileByBytes(dataBytes, BASE_PATH, filename + "-" + fileseq);

	}

	/**
	 * 合并分片
	 * 
	 * @param buf
	 */
	private void merge(ByteBuf buf) {
		// 业务的报文处理
		// 解析
		// 定义协议 PS：x表示占几个字节
		// [[版本x2][报文总长度x2][消息类型x1][文件标识x16][文件名长度x1][文件名][分片总数x2][文件总大小x8]]
		// 消息类型 0x01 上传文件 0x02 合并文件
		byte[] filenameBytes = new byte[16];
		buf.readBytes(filenameBytes);// 读取16字节是文件名
		String filename = ByteUtils.bytesToHex(filenameBytes);// 我这里是md5不会有中文没有乱码的问题

		short fileseq = buf.readUnsignedByte();// 读取1字节是文件名长度

	}

	/**
	 * TODO 将Byte数组转换成文件
	 * 
	 * @param bytes
	 * @param filePath
	 * @param fileName
	 */
	public static void createFileByBytes(byte[] bytes, String filePath, String fileName) {
		BufferedOutputStream bos = null;
		FileOutputStream fos = null;
		File file = null;
		try {
			File dir = new File(filePath);
			if (!dir.exists()) {// 判断文件目录是否存在
				dir.mkdirs();
			}
			file = new File(filePath + fileName);
			fos = new FileOutputStream(file);
			bos = new BufferedOutputStream(fos);
			bos.write(bytes);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (bos != null) {
				try {
					bos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

}
